use std::ffi::OsString;
use std::{collections::HashMap, path::PathBuf};

use fltk::{
    app::Sender,
    button::Button,
    dialog::{FileDialog, FileDialogOptions, FileDialogType},
    enums::{Align, Color},
    frame::Frame,
    group::{Flex, Group},
    prelude::{GroupExt, InputExt, WidgetBase, WidgetExt},
};

use super::super::{myapp::Message, style};

use fltk::input::Input;
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
pub enum Step1Message {
    Enter,
    // Modified,
    Done { target_dir: String },
}

pub struct Step1Tab {
    panel: Flex,
    // target_dir_input: Input,
    start_btn: Button,
    sender: Sender<Message>,
    inner: Rc<RefCell<Step1Inner>>,
}

struct Step1Inner {
    target_dir: String,
    start_btn: Button,
    hints_label: Frame,
    // target_dir_input: Input,
    disk_freespace: HashMap<OsString, f32>,
}

impl Step1Tab {
    const DEFAUL_TARGET_DIR: &'static str = r#"C:\TGBAWork"#;

    pub fn new(group: &mut Group, sender: Sender<Message>) -> Self {
        let mut panel = Flex::default_fill().column();

        panel.resize(group.x(), group.y(), group.w(), group.h());
        group.add(&panel);

        panel.set_margins(0, 20, 20, 20);

        Frame::default();

        let mut choose_btn: Button;
        let mut target_dir_input: Input;
        let start_btn: Button;

        let mut input_row = Flex::default().row();
        {
            panel.fixed(&input_row, 30);

            let label = Frame::default()
                .with_label("安装目标目录：")
                .with_align(Align::Inside | Align::Left);
            // label.set_label_font(style.font_zh);
            input_row.fixed(&label, 110);

            target_dir_input = Input::default();
            target_dir_input.set_value(Step1Tab::DEFAUL_TARGET_DIR);
            // target_dir_input.set_text_font(style.font_bold_en);
            target_dir_input.set_text_size(16);

            choose_btn = Button::default().with_label("选择..");
            // choose_btn.set_label_font(style.font_bold_zh);
            choose_btn.set_label_color(style::COLOR_DARKGREY);
            choose_btn.set_size(60, choose_btn.height());
            input_row.fixed(&choose_btn, 60);

            input_row.end();
        }

        let mut hints_label;
        let mut hints_row = Flex::default().row();
        hints_row.set_margins(0, 0, 0, 0);
        {
            panel.fixed(&hints_row, 12);

            let label = Frame::default();
            hints_row.fixed(&label, 110);

            hints_label = Frame::default().with_align(Align::Inside | Align::Left);
            hints_label.set_label_color(style::COLOR_DARKGREY);

            let label = Frame::default();
            hints_row.fixed(&label, 60);

            hints_row.end();
        }

        let frame = Frame::default();
        panel.fixed(&frame, 30);

        let mut btn_row = Flex::default().row();
        {
            panel.fixed(&btn_row, 30);

            Frame::default();

            start_btn = Button::default().with_label("开始安装");
            // start_btn.set_label_font(style.font_bold_zh);
            btn_row.fixed(&start_btn, 120);

            let frame = Frame::default();
            btn_row.fixed(&frame, 60);

            btn_row.end()
        }

        Frame::default();

        panel.end();

        let mut obj = Step1Tab {
            panel,
            start_btn: start_btn.clone(),
            sender,
            inner: Rc::new(RefCell::new(Step1Inner {
                target_dir: "".to_string(),
                start_btn,
                hints_label,
                disk_freespace: create_available_space_map(),
            })),
        };

        {
            // log::info!("开始检查系统剩余可用空间");

            obj.inner
                .borrow_mut()
                .set_directory(Step1Tab::DEFAUL_TARGET_DIR.to_string());

            let _inner = obj.inner.clone();
            target_dir_input.set_trigger(fltk::enums::CallbackTrigger::Changed);
            target_dir_input.set_callback(move |i| {
                _inner.borrow_mut().set_directory(i.value().to_string());
            });

            let _inner = obj.inner.clone();
            choose_btn.set_callback(move |_| {
                use FileDialogType::BrowseSaveDir;
                let mut dlg = FileDialog::new(BrowseSaveDir);

                let path = PathBuf::from(_inner.borrow().target_dir.clone());
                if let Err(err) = dlg.set_directory(&path) {
                    log::error!("Error in selecting target directory: {}", err.to_string());
                    eprintln!("{}", err);
                }

                dlg.set_option(FileDialogOptions::NewFolder);
                dlg.show();

                let filename = dlg.filename().to_string_lossy().to_string();

                if !filename.is_empty() {
                    target_dir_input.set_value(&filename);
                    log::info!("选择的目录: {}", &filename);
                    _inner.borrow_mut().set_directory(filename);
                }
            });
        }

        let _inner = obj.inner.clone();
        let s = obj.sender.clone();
        obj.start_btn.set_callback({
            move |_| {
                s.send(Message::Step1(Step1Message::Done {
                    target_dir: _inner.borrow().target_dir.clone(),
                }))
            }
        });

        log::info!("step1 panel created");

        obj
    }

    pub fn widget(&self) -> &Flex {
        &self.panel
    }

    // pub fn handle_message(&mut self, _msg: Step1Message) {
    //     // println!("handle: {} msg: {:?}", self.a_no, msg);
    // }
}

impl Step1Inner {
    fn set_directory(&mut self, target: String) {
        let current = &mut self.target_dir;
        if *current != target {
            *current = target;

            log::info!("设置安装目录：{}", current);
            if !check_availabel_space(&mut self.hints_label, &current, &self.disk_freespace) {
                self.start_btn.deactivate();
            } else {
                self.start_btn.activate();
            }
        }
    }
}

fn check_availabel_space(
    hints_label: &mut Frame,
    path: &str,
    map: &HashMap<OsString, f32>,
) -> bool {
    let path = PathBuf::from(path);
    use std::path::Component::{Normal, Prefix, RootDir};

    let expected_space = 3.0f32;

    let mut errmsg = "无效路径";
    let mut components = path.components();
    loop {
        let Some(Prefix(prefix)) = components.next() else {
            errmsg = "无效路径";
            break;
        };

        let Some(RootDir) = components.next() else {
            errmsg = "不能使用相对路径";
            break;
        };

        let Some(Normal(_)) = components.next() else {
            errmsg = "不能安装在根目录";
            break;
        };

        let driver = prefix.as_os_str().to_os_string();
        if let Some(freespace) = map.get(&driver.to_ascii_uppercase()) {
            if expected_space < *freespace {
                let msg = format!(
                    "安装所需空间约 {} GiB 内，剩余空间 {:.1} GiB",
                    expected_space, freespace
                );
                hints_label.set_label(&msg);
                hints_label.set_label_color(Color::from_rgb(5, 100, 5));
                log::info!("{msg}");

                return true;
            } else {
                hints_label.set_label(&format!(
                    "存储空间不足，安装所需空间约 {} GiB 内，剩余空间 {:.1} GiB",
                    expected_space, freespace
                ));
                hints_label.set_label_color(Color::from_rgb(200, 0, 0));

                return false;
            }
        }
        break;
    }

    hints_label.set_label(&errmsg);
    hints_label.set_label_color(Color::from_rgb(200, 0, 0));
    return false;
}

fn create_available_space_map() -> HashMap<OsString, f32> {
    use std::path::Component;
    use sysinfo::Disks;
    let mut available_space: HashMap<OsString, f32> = HashMap::new();
    let disks = Disks::new_with_refreshed_list();

    for disk in disks.list() {
        match disk.mount_point().components().next() {
            Some(Component::Prefix(prefix_component)) => {
                let driver = prefix_component.as_os_str().to_os_string();
                let free = disk.available_space() as f32 / (1024.0 * 1024.0 * 1024.0);
                available_space.insert(driver, free);
            }
            _ => {}
        }
    }

    available_space
}
